#pragma once

// ARMADILLO
#include <armadillo>
// BOOST
#include <boost/program_options.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

using namespace std;
using namespace arma;
namespace po = boost::program_options;
namespace posix = boost::posix_time;

// ARMADILLO
template<typename MAT> 
class ARMA {
 public:
  static void getSize(const MAT& A, int& m, int& n);
  static int getSize(const MAT& A);
};

template<> 
class ARMA<mat> {
 public:
  static void getSize(const mat& A, int& m, int& n) {
    m = A.n_rows;
    n = A.n_cols;
  }
  static int getSize(const vec& A) {
    return A.n_elem;
  }
  static double sparsityCount(const mat& A) {
    return accu(abs(A) > 1e-6);
  }
  static double sparsity(const mat& A) {
    return sparsityCount(A) / A.n_rows / A.n_cols;
  }
};

template<> 
class ARMA<sp_mat> {
 public:
  static void getSize(const sp_mat& A, int& m, int& n) {
    m = A.n_rows;
    n = A.n_cols;
  }
};

template<> 
class ARMA<vec> {
 public:
  static int getSize(const vec& A) {
    return A.n_elem;
  }
};

// STL
#define FORE(i, f, l) for (int i = (f); i < (int)(l); i++)
#define FORI(i, f, l) for (int i = (f); i <= (int)(l); i++)
#define SIZE(v) ((int)(v).size())
#define ALL(v) (v).begin(), (v).end()

#define MSG(m) cout << m << endl
#define ERROR_MSG(m) cerr << m << endl
#define FATAL(m) { ERROR_MSG("in " << __FILE__ << " on line " << __LINE__ << ": " << m); exit(1); }

// Program Options
#define REQUIRE_PARAM(p) if (!vm.count(p)) FATAL("Need parameter --" << (p))

// POSIX TIME
#define CLOCK posix::microsec_clock::local_time()
#define SECONDS(d) ((double) (d).total_milliseconds() / 1000)
